package com.it.admin.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.it.admin.convert.ArticleDetailConvert;
import com.it.admin.event.DeleteArticleEvent;
import com.it.admin.event.PublishArticleEvent;
import com.it.admin.event.UpdateArticleEvent;
import com.it.admin.model.dto.article.*;
import com.it.admin.model.vo.article.FindArticleDetailRspVO;
import com.it.admin.model.vo.article.FindArticlePageListRspVO;
import com.it.admin.service.AdminArticleService;
import com.it.common.domain.dos.*;
import com.it.common.enums.ResponseCodeEnum;
import com.it.common.exception.BizException;
import com.it.common.mapper.*;
import com.it.common.utils.PageResponse;
import com.it.common.utils.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AdminArticleServiceImpl extends ServiceImpl<ArticleMapper, ArticleDO> implements AdminArticleService {

    @Resource
    private ArticleTagRelMapper articleTagRelMapper;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private ArticleContentMapper articleContentMapper;

    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private ArticleCategoryRelMapper articleCategoryRelMapper;

    @Resource
    private TagMapper tagMapper;

    @Resource
    private ApplicationEventPublisher eventPublisher;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response publishArticle(PublishArticleDto publishArticleDto) {
        // 1. Dto 转 ArticleDO, 并保存
        ArticleDO articleDO = ArticleDO.builder().cover(publishArticleDto.getCover())
                .title(publishArticleDto.getTitle()).summary(publishArticleDto.getSummary())
                .createTime(LocalDateTime.now()).updateTime(LocalDateTime.now()).build();

        articleMapper.insert(articleDO);

        // 拿到插入记录的主键 ID
        Long articleId = articleDO.getId();
        // 2. Dto 转 ArticleContentDO，并保存
        ArticleContentDO articleContentDO = ArticleContentDO.builder()
                .content(publishArticleDto.getContent())
                .articleId(articleId).build();
        articleContentMapper.insert(articleContentDO);
        // 3. 处理文章关联的分类
        Long categoryId = publishArticleDto.getCategoryId();
        // 3.1 校验提交的分类是否真实存在
        CategoryDO categoryDO = categoryMapper.selectById(categoryId);
        if (Objects.isNull(categoryDO)) {
            log.warn("==> 分类不存在, categoryId: {}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }
        ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder()
                .categoryId(categoryId).articleId(articleId).build();

        articleCategoryRelMapper.insert(articleCategoryRelDO);

        List<String> publishTags = publishArticleDto.getTags();

        insertTags(articleId,publishTags);

        // 发送文章发布事件
        eventPublisher.publishEvent(new PublishArticleEvent(this,articleId));

        return Response.success();

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteArticle(DeleteArticleDto deleteArticleDto) {
        Long articleId = deleteArticleDto.getId();

        //删除文章
        articleMapper.deleteById(articleId);

        // 2. 删除文章内容
        articleContentMapper.deleteByArticleId(articleId);

        // 3. 删除文章-分类关联记录
        articleCategoryRelMapper.deleteByArticleId(articleId);

        // 4. 删除文章-标签关联记录
        articleTagRelMapper.deleteByArticleId(articleId);

        //发送文章删除事件
        eventPublisher.publishEvent(new DeleteArticleEvent(this,articleId));

        return Response.success();
    }

    @Override
    public Response findArticlePageList(FindArticlePageListDto findArticlePageListDto) {
        Long current = findArticlePageListDto.getCurrent();
        Long size = findArticlePageListDto.getSize();
        String title = findArticlePageListDto.getTitle();
        LocalDate startDate = findArticlePageListDto.getStartDate();
        LocalDate endDate = findArticlePageListDto.getEndDate();
        Integer type = findArticlePageListDto.getType();

        // 执行分页查询
        Page<ArticleDO> articleDOPage = articleMapper.selectPageList(current, size, title, startDate, endDate,type);

        List<ArticleDO> articleDOS = articleDOPage.getRecords();
        // DO 转 VO
        List<FindArticlePageListRspVO> vos = null;
        if(!CollectionUtils.isEmpty(articleDOS)){
            vos = articleDOS.stream().map(articleDO -> {
                FindArticlePageListRspVO vo = new FindArticlePageListRspVO();
                vo.setCover(articleDO.getCover());
                vo.setTitle(articleDO.getTitle());
                vo.setId(articleDO.getId());
                vo.setCreateTime(LocalDateTime.now());
                vo.setIsTop(articleDO.getWeight() > 0);
                return vo;
            }).collect(Collectors.toList());
        }
        return PageResponse.success(articleDOPage, vos);
    }

    /**
     * 查询文章详情
     *
     * @param findArticleDetailDto
     * @return
     */
    @Override
    public Response findArticleDetail(FindArticleDetailDto findArticleDetailDto) {

        Long articleId = findArticleDetailDto.getId();

        ArticleDO articleDO = articleMapper.selectById(articleId);

        if (Objects.isNull(articleDO)) {
            log.warn("==> 查询的文章不存在，articleId: {}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }

        ArticleContentDO articleContentDO = articleContentMapper.selectByArticleId(articleId);

        ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId);

        List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId);

        // 获取对应标签 ID 集合
        List<Long> tagIds = articleTagRelDOS.stream().map(articleTagRelDO -> articleTagRelDO.getTagId()).collect(Collectors.toList());
        // DO 转 VO
        FindArticleDetailRspVO vo = ArticleDetailConvert.INSTANCE.convertDO2VO(articleDO);
        vo.setContent(articleContentDO.getContent());
        vo.setCategoryId(articleCategoryRelDO.getCategoryId());
        vo.setTagIds(tagIds);

        return Response.success(vo);
    }

    @Override
    public Response updateArticle(UpdateArticleDto updateArticleDto) {

        Long articleId = updateArticleDto.getId();
        // 1. Dto 转 ArticleDO, 并更新
        ArticleDO articleDO = ArticleDO.builder()
                .cover(updateArticleDto.getCover())
                .id(articleId)
                .updateTime(LocalDateTime.now())
                .summary(updateArticleDto.getSummary())
                .title(updateArticleDto.getTitle()).build();

        int count = articleMapper.updateById(articleDO);
        if (count != 1) {
            log.warn("==> 该文章不存在, articleId: {}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }
        // 2. Dto 转 ArticleContentDO，并更新
        ArticleContentDO articleContentDO = ArticleContentDO.builder()
                .articleId(articleId)
                .content(updateArticleDto.getContent())
                .build();
        articleContentMapper.updateByArticleId(articleContentDO);
        // 3. 更新文章分类
        Long categoryId = updateArticleDto.getCategoryId();
        // 3.1 校验提交的分类是否真实存在
        CategoryDO categoryDO = categoryMapper.selectById(categoryId);
        if (Objects.isNull(categoryDO)) {
            log.warn("===> 分类不存在, categoryId: {}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }
        // 先删除该文章关联的分类记录，再插入新的关联关系
        articleCategoryRelMapper.deleteByArticleId(articleId);
        ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder()
                .categoryId(categoryId).articleId(articleId).build();
        articleCategoryRelMapper.insert(articleCategoryRelDO);
        // 4. 保存文章关联的标签集合
        // 先删除该文章对应的标签
        articleTagRelMapper.deleteByArticleId(articleId);
        List<String> publishTags = updateArticleDto.getTags();
        insertTags(articleId, publishTags);

        eventPublisher.publishEvent(new UpdateArticleEvent(this,articleId));

        return Response.success();
    }

    @Override
    public Response updateArticleIsTop(UpdateArticleIsTopDto updateArticleIsTopDto) {
        Long articleId = updateArticleIsTopDto.getArticleId();
        Boolean isTop = updateArticleIsTopDto.getIsTop();
        // 默认权重为 0
        Integer weight = 0;
        // 若设置为置顶
        if (isTop) {
            // 查询出表中最大的权重值
            ArticleDO articleDO = articleMapper.selectMaxWeight();
            Integer maxWeight = articleDO.getWeight();
            // 最大权重值加一
            weight = maxWeight + 1;
        }
        ArticleDO articleDO = ArticleDO.builder().id(articleId)
                .weight(weight).build();
        articleMapper.updateById(articleDO);
        return Response.success();
    }

    /**
     * 保存标签
     * @param articleId
     * @param publishTags
     */
    private void insertTags(Long articleId, List<String> publishTags) {
        // 筛选提交的标签（表中不存在的标签）
        List<String> notExistTags = new ArrayList<>();
        // 筛选提交的标签（表中已存在的标签）
        List<String> existedTags = new ArrayList<>();

        //创建者
        String creator = SecurityContextHolder.getContext().getAuthentication().getName();

        //查询所有标签
        List<TagDO> tagDOS = tagMapper.findAll();

        if (CollectionUtils.isEmpty(tagDOS)) {
            notExistTags = publishTags;
        } else {
            List<String> tagIds = tagDOS.stream().map(tagDO -> String.valueOf(tagDO.getId())).collect(Collectors.toList());
            // 通过标签 ID 来筛选，包含对应 ID 则表示提交的标签是表中存在的
            existedTags = publishTags.stream().filter(s -> tagIds.contains(s)).collect(Collectors.toList());
            // 否则则是不存在的
            notExistTags = publishTags.stream().filter(s -> !tagIds.contains(s)).collect(Collectors.toList());

            // 还有一种可能：按字符串名称提交上来的标签，也有可能是表中已存在的，比如表中已经有了 Java 标签，用户提交了个 java 小写的标签，需要内部装换为 Java 标签
            Map<String, Long> tagMap = tagDOS.stream().collect(Collectors.toMap(tagDO -> tagDO.getName().toLowerCase(), tagDO -> tagDO.getId()));

            // 使用迭代器进行安全的删除操作
            Iterator<String> iterator = notExistTags.iterator();
            while (iterator.hasNext()) {
                String notExistTag = iterator.next();
                if (tagMap.containsKey(notExistTag.toLowerCase())) {
                    iterator.remove();
                    existedTags.add(String.valueOf(tagMap.get(notExistTag.toLowerCase())));
                }
            }
            // 将提交的上来的，已存在于表中的标签，文章-标签关联关系入库
            if(!CollectionUtils.isEmpty(existedTags)){
                List<ArticleTagRelDO> articleTagRelDOS = new ArrayList<>();
                for(String tagId : existedTags){
                    ArticleTagRelDO articleTagRelDO = new ArticleTagRelDO();
                    articleTagRelDO.setArticleId(articleId);
                    articleTagRelDO.setTagId(Long.valueOf(tagId));
                    articleTagRelDOS.add(articleTagRelDO);
                }
                articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS);
            }
            // 将提交的上来的，不存在于表中的标签，入库保存
            if(!CollectionUtils.isEmpty(notExistTags)){
                List<ArticleTagRelDO> articleTagRelDOS = new ArrayList<>();
                for(String tagName : notExistTags){
                    TagDO tagDO = new TagDO();
                    tagDO.setName(tagName);
                    tagDO.setUpdateTime(LocalDateTime.now());
                    tagDO.setCreator(creator);
                    tagDO.setCreateTime(LocalDateTime.now());
                    tagMapper.insert(tagDO);
                    Long tagId = tagDO.getId();
                    ArticleTagRelDO articleTagRelDO = ArticleTagRelDO.builder()
                            .tagId(tagId).articleId(articleId).build();

                    articleTagRelDOS.add(articleTagRelDO);
                }
                articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS);
            }
        }
    }
}
